package cryptography.asymmetric.RSA;

import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.KeyFactory;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class RSACryptography {

	private static final String ALGORITHM = "DSA";
	private static PrivateKey PRIVATE_KEY;
	private static PublicKey PUBLIC_KEY;

	private static void generateKeys(String keyAlgorithm, int numBits)
			throws Exception {

		try {

			// Get the public/private key pair
			KeyPairGenerator keyGen = KeyPairGenerator
					.getInstance("DSA", "SUN");
			keyGen.initialize(numBits);
			KeyPair keyPair = keyGen.genKeyPair();
			PRIVATE_KEY = keyPair.getPrivate();
			PUBLIC_KEY = keyPair.getPublic();

			System.out.println("\n" + "Generating key/value pair using "
					+ PRIVATE_KEY.getAlgorithm() + " algorithm");

			// Get the bytes of the public and private keys
			byte[] privateKeyBytes = PRIVATE_KEY.getEncoded();
			byte[] publicKeyBytes = PUBLIC_KEY.getEncoded();

			// Get the formats of the encoded bytes
			String formatPrivate = PRIVATE_KEY.getFormat(); // PKCS#8
			String formatPublic = PUBLIC_KEY.getFormat(); // X.509

			System.out.println("  Private Key Format : " + formatPrivate);
			System.out.println("  Public Key Format  : " + formatPublic);

			// The bytes can be converted back to public and private key objects
			KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
			EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
					privateKeyBytes);
			PrivateKey privateKey2 = keyFactory.generatePrivate(privateKeySpec);

			EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
					publicKeyBytes);
			PublicKey publicKey2 = keyFactory.generatePublic(publicKeySpec);

			// The original and new keys are the same
			System.out.println("  Are both private keys equal? "
					+ PRIVATE_KEY.equals(privateKey2));

			System.out.println("  Are both public keys equal? "
					+ PUBLIC_KEY.equals(publicKey2));

		} catch (InvalidKeySpecException specException) {

			System.out.println("Exception");
			System.out.println("Invalid Key Spec Exception");

		} catch (NoSuchAlgorithmException e) {

			System.out.println("Exception");
			System.out.println("No such algorithm: " + keyAlgorithm);

		}

	}

	public static void main(String[] args) throws Exception {

		// Generate a 1024-bit Digital Signature Algorithm (DSA) key pair
		// generateKeys("DSA", 1024);

		// Generate a 576-bit DH key pair
		// generateKeys("DH", 576);

		// Generate a 1024-bit RSA key pair
		generateKeys(ALGORITHM, 1024);

		String password = "mypassword";

		Cipher c = Cipher.getInstance(PRIVATE_KEY.getAlgorithm());
		c.init(Cipher.ENCRYPT_MODE, PUBLIC_KEY);
		byte[] encValue = c.doFinal(password.getBytes());
		String encryptedValue = new BASE64Encoder().encode(encValue);

		c.init(Cipher.DECRYPT_MODE, PRIVATE_KEY);
		byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue);
		byte[] decValue = c.doFinal(decordedValue);
		String decryptedValue = new String(decValue);

		System.out.println("Plain Text : " + password);
		System.out.println("Encrypted : " + encryptedValue);
		System.out.println("Decrypted : " + decryptedValue);
		System.out.println("Private Key : " + PRIVATE_KEY);
		System.out.println("Public key : " + PUBLIC_KEY);
	}
}
